<?php
/**
 * Daemons Factory
 *
 * PHP version 5
 *
 * @category   Web
 * @package    Tourstart
 * @subpackage Util
 * @author     Alex Slubsky <aslubsky@gmail.com>
 * @license    Tourstart License http://tourstart.dk/license
 * @version    SVN: $Id: $
 *
 *
 */

/**
 * Includes
 */

/**
 * Video converter
 *
 * PHP version 5
 *
 * @category   Web
 * @package    Tourstart
 * @subpackage Search
 * @author     Alex Slubsky <aslubsky@gmail.com>
 * @license    Tourstart License http://tourstart.dk/license
 * @version    Release: $Id: $
 *
 *
 */
declare(ticks=1);
 
class DaemonFactory extends Object
{
    const ErrorLimit = 100;
    
    protected $pidFile;
    
    protected $tmpFile;
    
    protected $terminated = false;
    
    protected $daemonName = null;
    
    protected $period = 0;
    
    protected $logger;
    
    protected $logsCount;
    
    protected static $daemonList = null;
  
    protected $memLimit;

    public function __construct()
    {        
        $this->logsCount = 0;
        
        $this->initParams();
        
        $this->logger = Logger::getLogger( $this->daemonName );
        $this->tmpFile = '/tmp/'.strtolower( $this->daemonName ).'.tmp';
        
        $this->checkParams();
        
        pcntl_signal(SIGTERM,array($this,'sigHandler'));
        
        $this->pidFile = self::getPidFile( $this->daemonName );
        file_put_contents( $this->pidFile, getmypid() );
        $this->setproctitle( strtolower( $this->daemonName ) );
        
        $this->memLimit = (intval(ini_get('memory_limit'))*1000000 / 1);
        //parent::__construct();
    }
    
    protected function initParams()
    {
        $params = $_SERVER['argv'];
        
        if(substr($params[1] ,0,2)=='-n') {
            $this->daemonName = substr( $params[1], 3, strlen($params[1]) );
        } else {
            throw new Exception( 'Daemon name is not set. Please use -n key (-n=Some_Daemon)' );
        }        
        
        if(substr($params[2] ,0,2)=='-p') {
            $this->period = intval( substr( $params[2], 3, strlen($params[2]) ) );
        }   
    }
    
    protected function checkParams()
    {
        if( !$this->daemonExists() ) {
           throw new Exception( 'Daemon '.$this->daemonName.' not found' );
        }
        
        $params = $_SERVER['argv'];
        switch(strtolower($params[2])) {
            case 'stop':
                $this->stop();
            break;
            case 'restart':
                $this->restart();
            break;            
        }
        
        if( self::isActive( $this->daemonName ) ) {
            $this->logger->info( 'Daemon '.$this->daemonName.' is already runed' );
            throw new Exception( 'Daemon '.$this->daemonName.' is already runed' );
        }
        
        if( $this->period < 1 ) {
            throw new Exception( 'Period is not valid. Must be > 1 second' );
        }  
    }
    
    protected function stop()
    {
        $pidFile = self::getPidFile( $this->daemonName );
        if( is_file($pidFile) ) {
            $pid = file_get_contents($pidFile);            
            if( !posix_kill($pid,SIGTERM) || !unlink( $pidFile ) ) {
                exit(-1);
            }
            exit(0);
        }
        exit(-1);
    }
    
    protected function restart()
    {
        $pidFile = self::getPidFile( $this->daemonName );
        if( is_file($pidFile) ) {
            $pid = file_get_contents($pidFile);            
            $this->period = $this->getPrevPeriod($pid);
            if( !posix_kill($pid,SIGTERM) || !unlink( $pidFile ) ) {
                exit(-1);
            }            
            return;
        }
        exit(-1);
    }
    
    protected function getPrevPeriod($pid)
    {
        if( file_exists($this->tmpFile) ) {
            return intval(file_get_contents($this->tmpFile));
        }           
        $comand = 'ps -auxfS | grep '.$pid;
        exec( $comand . " 2>&1", $res );
        //print_r( $res ); print "\n";
        $period = 0;
        foreach( $res as $cmd ) {
            if( strstr( $cmd, 'grep '.$pid ) !== false ) {
                continue;
            }
            
            $parts = explode(' ',$cmd);
            $period = explode('=',end($parts));
            $period = intval($period[1]);
        }
        
        file_put_contents($this->tmpFile,$period);
        //$this->logger->debug( 'Set '.$this->tmpFile.','.$period );
        return $period;
    }
    
    protected function daemonExists()
    {
        if(is_null($this->daemonName)) {
            return false;
        }
        
        if( is_null(self::$daemonList) ) {
            self::$daemonList = array();
            $classes = get_declared_classes();
            foreach ($classes as $class) {
                if (typeOf($class)->isSubclassOf('DaemonAbstract')) {
                    self::$daemonList[] = $class;
                }
            }
        }

        return in_array($this->daemonName,self::$daemonList);
    }
    
    public static function isActive( $daemonName )
    {
        $pidFile = self::getPidFile( $daemonName );
        if( is_file($pidFile) ) {
            $pid = file_get_contents($pidFile);            
            if(posix_kill($pid,0)) {
                return true;
            } elseif( !unlink( $pidFile ) ) {
                exit(-1);
            }
        }
        return false;
    }
    
    public static function getPidFile( $daemonName )
    {
        return '/tmp/'.strtolower( $daemonName ).'.pid';
    }
    
    public function sigHandler($signo) 
    {
        switch($signo)  {
            case SIGTERM: 
                $this->terminated = true;
            break;        
        }
    }
    
    public function setproctitle($title)
    {
        if( function_exists('setproctitle') ) {
            return setproctitle($title);
        }
        
        return false;
    }    
    
    public function run()
    {
        $obj = $this->initDaemon();
        
        while(!$this->terminated) {
            try {
                $obj->work();
            } catch(Exception $exception) {
                $this->logsCount++;
                if( $this->logsCount > self::ErrorLimit ) {
                    $this->logger->fatal( 'Daemon crashed', $this );
                    exit('Daemon crashed');
                }
                $this->logger->error( Error::getFormatError($exception) );
            }            
            if( memory_get_usage() > $this->memLimit || $obj->NeedRestart ) {
                $this->logger->info( date('m-d H:i:s') .' Daemon was restarted. Memory - '. memory_get_usage() .', from limit - '.$this->memLimit );
                exec('php '.dirname(__FILE__).'/run.php -n='.$this->daemonName.' restart > /dev/null 2>/dev/null &');
            }            
            sleep( $this->period );            
        }
    }
    
    protected function initDaemon()
    {
        return new $this->daemonName();
    }
}